from OpenTonitruum.Base.DataStructure import Event, Struct
from OpenTonitruum.Protocol.Equipment import Equipment
from OpenTonitruum.Protocol.PhysicalLayer import PhysicalLayer
from OpenTonitruum.Signal.TransmissionSystem import TransmissionSystem
from OpenTonitruum.Source.DataSource import SourceLayer
from threading import Thread
from SimPy.Simulation import *

from OpenTonitruum.Controller.Parameter import ParameterItem, ParameterException

class Environment(object):
	def __init__(self):
		self.parameters = Struct()
		self.listeners = Struct()
		self.runner = EnvironmentRunner(self.parameters, listeners)
	
	def requiredParameters(self):
		return [
			ParameterItem(self, "Time limit for simulation [seg]", "parameters.simulationtime", "10000", float),
			
			ParameterItem(self, "Transmitting time by bit [seg]", "parameters.transmittingTimeByBit", "1.0", float),
			ParameterItem(self, "Processing time by bit [seg]", "parameters.processingTimeByBit", "1.0", float),
			ParameterItem(self, "Parallel bit number on transmission system", "parameters.parallelBitNumber", "4", int),
			
			ParameterItem(self, "Link protocol name", "parameters.linkprotocol", "HDLC", str),
			ParameterItem(self, "EEG equipment ID", "parameters.eegEquipmentID", "4", int),
			ParameterItem(self, "Mobile equipment ID", "parameters.mobileEquipmentID", "4", int),
			
			ParameterItem(self, "Activated EEG signal auto-generating", "parameters.autoSource", "0", int),
			ParameterItem(self, "File path EEG source", "parameters.sourceFilename", ""),
			ParameterItem(self, "EEG source scale value [V]", "parameters.sourceScaleValue", "1e-6", float),
			ParameterItem(self, "EEG source frequency sampling value [Hz]", "parameters.sourceFrequencySample", "250", float),
			
			ParameterListener(self, "", "listeners."):
		]
	
	def run(self):
		if not self.parameters.autoSource and self.parameters.sourceFilename.strip() == "":
			raise ParameterException('Data source needs a valid file path.')
		if self.runner.isAlive():
			print ">> Resuming thread"
			self.runner.restart()
		else:
			print ">> Starting thread"
			self.runner.start()
	
	def stop(self):
		if self.runner.isAlive():
			self.runner.stop()
	
	def reset(self):
		self.stop()
		self.run()
	
	def finalizeThread(self):
		self.runner.finalizeThread()
		print ">> Closing thread"

class EnvironmentSimulation(Process):
	def __init__(self, parameters, listeners):
		Process.__init__(self)
		self.parameters = parameters
		self.listeners = listeners
	
	def createVariables(self):
		self.transmissionSystem = TransmissionSystem()
		self.physicalLayer = PhysicalLayer(
			transmissionSystem = self.transmissionSystem,
			transmittingTimeByBit = self.parameters.transmittingTimeByBit,
			processingTimeByBit = self.parameters.processingTimeByBit,
			parallelBitNumber = self.parameters.parallelBitNumber
		)
		self.eegEquipment = Equipment(
			id = self.parameters.eegEquipmentID,
			protocol = self.parameters.linkprotocol,
			physicalLayer = self.physicalLayer
		)
		self.mobileEquipment = Equipment(
			id = self.parameters.mobileEquipmentID,
			protocol = self.parameters.linkprotocol,
			physicalLayer = self.physicalLayer
		)
		self.sourceLayer = SourceLayer(
			isAuto = self.parameters.autoSource,
			filename = self.parameters.sourceFilename,
			scale = self.parameters.sourceScaleValue,
			frequency_sample = self.parameters.sourceFrequencySample
		)
		
		activate(self.physicalLayer, self.physicalLayer.run())
		activate(self, self.process())
	
	def process(self):
		for time, value in self.sourceLayer:
			if not self.active: return
			self.eegEquipment.send(self.parameters.mobileEquipmentID, [value])
			if not self.active: return
			yield hold, self, time
	
	def run(self):
		initialize()
		self.createVariables()
		simulate(until = self.parameters.simulationtime)
		print "Simulation ended"
	
	def stop(self):
		stopSimulation()
		
class EnvironmentRunner(Thread):
	def __init__(self, parameters, listeners):
		Thread.__init__(self)
		self.active = False
		self.finalize = False
		self.listeners = listeners
		self.parameters = parameters
	
	def run(self):
		self.active = True
		while True:
			if self.active:
				self.env = EnvironmentSimulation(self.parameters, self.listeners)
				self.env.run()
				self.active = False
			if self.finalize:
				return
	
	def stop(self):
		self.active = False
		self.env.stop()
	
	def restart(self):
		self.active = True
		
	def finalizeThread(self):
		self.finalize = True
		
